#include "charge.h"

static charger_t _charger;
static os_thread_t _thread_send_recv;
static os_thread_t _thread_period;


void charge_start(charger_t *charger, gun_t *curr_gun, uint16_t seq);
void charge_stop(charger_t *charger, gun_t *curr_gun, uint16_t seq);
void charge_end(charger_t *charger, gun_t *curr_gun, uint16_t seq);

extern int32_t net_sync(charger_t *charger);
extern void net_diss_handle(charger_t *charger);
extern void save_ql_fee( price_t * p_ql_fee);

static int32_t param_save(local_config_t *cfg)
{
    int32_t ret = -1;

    if (LOCAL_CONFIG_MAGIC == cfg->magic)
    {
        ret = os_persist_data_write(cfg, sizeof(local_config_t));
    }

    return ret;
}

static int price_save(local_config_t *cfg)
{
    int ret = 0;

    if (LOCAL_CONFIG_MAGIC == cfg->magic)
    {
        ret = os_persist_data_write(cfg, sizeof(local_config_t));
    }
    else if (0 == cfg->magic)
    {
        cfg->magic = LOCAL_CONFIG_MAGIC;
        ret = os_persist_data_write(cfg, sizeof(local_config_t));
    }
    else
    {
        log_warn("price save repeat, magic:%08x\r\n", cfg->magic);
    }

    return ret;
}


//设置对应时段的费率价格
void price_set(charger_t *charger, price_t *p)
{
    local_config_t *cfg = &charger->local_cfg;

    log_debug("电费 ： 尖:%d, 高:%d, 平:%d, 谷:%d\r\n", p->price_elec[0], p->price_elec[1], p->price_elec[2], p->price_elec[3]);
    log_debug("服务费 ： 尖:%d, 高:%d, 平:%d, 谷:%d\r\n", p->price_service[0], p->price_service[1], p->price_service[2], p->price_service[3]);

    proto_hex_dump("price", p->period, 48);

    save_ql_fee(p);

    if (memcmp(&(cfg->price), p, sizeof(price_t)) == 0)
    {
        log_debug("price elec same\r\n");
        return ;
    }
    else
    {
        log_debug("update price elec\r\n");
        memcpy(&(cfg->price), p, sizeof(price_t));
        price_save(cfg);
    }
}

//获取对应时段的费率价格
static void price_get(local_config_t *cfg, uint16_t *elec_price, uint16_t *service_price)
{
#define BEIJING_TIME_ZONE   0 // (8*3600)
#define SECONDS_OF_A_DAY    86400

    uint32_t g_seconds_of_today = (os_get_time() + BEIJING_TIME_ZONE) % SECONDS_OF_A_DAY;//距离0点的秒钟数
    uint8_t period = (uint8_t)(g_seconds_of_today / 1800);//当前时间处于48个时段中的第几个
    uint8_t which_price = 0;

    which_price = cfg->price.period[period] & 0x03;//当前时段采用尖、高、平、谷哪种费率
    *elec_price = cfg->price.price_elec[which_price];//当前电费费率是多少

    which_price = (cfg->price.period[period] & 0x0C) >> 2;
    *service_price = cfg->price.price_service[which_price];

    log_debug("current price period[%d], which:[%d], elec:%d, serv:%d \n", period, which_price, *elec_price, *service_price);
}

//获取当前时段
uint8_t time_get_period(void)
{
#define BEIJING_TIME_ZONE   (8*3600)
#define SECONDS_OF_A_DAY    86400
unsigned int i;
//	i=os_get_time() + (8*3600);
	i=os_get_time() ;

	int g_seconds_of_today=i%SECONDS_OF_A_DAY;
    //uint32_t g_seconds_of_today = (os_get_time() + (8*3600)) % SECONDS_OF_A_DAY;//距离0点的秒钟数
    printf("i=====%d g_seconds_of_today====%d  ",i,g_seconds_of_today);
    uint8_t period = (int)(g_seconds_of_today / 1800);//当前时间处于48个时段中的第几个
    printf("BEIJING_TIME_ZONE====%d\n",BEIJING_TIME_ZONE);
    log_debug("current g_seconds_of_today  %d \n", g_seconds_of_today);

    return period;
}

static void bill_calculate(charger_t *charger, gun_t *gun)
{
    uint8_t is_stop = 0;
    uint16_t curr_elec_fee = 0x00;
    uint16_t curr_service_fee = 0x00;
    uint32_t curr_kwh = 0x00;
    uint32_t difference_kwh = 0x00;         //每次循环加的电量
    uint32_t calc_fee_elec = 0;
    uint32_t calc_fee_service = 0;
    uint32_t reduce_kwh = 0;
    uint8_t  period_idx = 0;
    uint32_t last_fee_elec = 0;
    uint32_t last_fee_service = 0;
    uint32_t last_total_kwh = 0;
    uint32_t last_period_kwh = 0;

    fee_t *p_fee = (fee_t *) & (gun->fee);
    account_t *p_account = (account_t *) & (gun->account);

    if (gun->gun_state != GUN_STATE_CHARGING)
    {
        return;
    }

    price_get(&charger->local_cfg, &curr_elec_fee, &curr_service_fee);

    curr_kwh = gun->u_gun->current_charge_kwh;
    difference_kwh = curr_kwh - p_fee->total_kwh - p_fee->start_kwh;
    period_idx = time_get_period();
    last_period_kwh = p_fee->period_power[period_idx];
    p_fee->period_power[period_idx] += difference_kwh;

    p_fee->total_time = (os_get_time() - p_fee->start_time)/60;// /60 单位:分钟
    last_fee_elec = p_fee->total_fee_elec;
    last_fee_service = p_fee->total_fee_service;
    last_total_kwh = p_fee->total_kwh;
    p_fee->total_fee_elec += difference_kwh * curr_elec_fee;
    p_fee->total_fee_service += difference_kwh * curr_service_fee;
    p_fee->total_kwh = (curr_kwh - p_fee->start_kwh);

    log_debug("gun_id:%d | cur_elec:%d, cur_serv:%d | start_kwh:%d, cur_kwh:%d, tot_kwh:%d | tot_time:%d | t_elec:%d, t_serv:%d |\r\n",
        gun->gun_id, curr_elec_fee, curr_service_fee, p_fee->start_kwh, curr_kwh,
        p_fee->total_kwh, p_fee->total_time, p_fee->total_fee_elec, p_fee->total_fee_service);

    do
    {
        if (gun->start_mode != START_MODE_PLATFORM)
        {
        	calc_fee_elec = (p_fee->total_fee_elec + 5000) / 10000;
        	calc_fee_service = (p_fee->total_fee_service + 5000) / 10000;
            if ((calc_fee_elec + calc_fee_service) >= p_account->balance)
            {
                p_fee->stop_code = CHARGE_STOP_BALANCE;
                log_debug("balance [%d] lower than [%d], stop charging\r\n", \
                    p_account->balance, (p_fee->total_fee_elec + p_fee->total_fee_service) / 10000);
                is_stop = 1;
                break;
            }
        }
#if 0 //仅仅测试使用
        if (gun->u_gun->remain_charge_time == 0)
        {
            p_fee->stop_code = CHARGE_STOP_FULL;
            is_stop = 1;
            log_debug("full charge, stop charging\r\n");
            break;
        }
#endif
        switch (p_account->type)
        {
        case TYPE_MONEY:
        	calc_fee_elec = (p_fee->total_fee_elec + 5000) / 10000;
        	calc_fee_service = (p_fee->total_fee_service + 5000) / 10000;
            if ((calc_fee_elec + calc_fee_service) >= p_account->value)
            {
                uint32_t fixed_kwh = 0;
                uint32_t fixed_money = (p_account->value * 10000 - last_fee_elec - last_fee_service);
                fixed_kwh = fixed_money / (curr_elec_fee + curr_service_fee); // 0.0001
                fixed_kwh = (fixed_kwh + 5)/10*10; // 0.001
                for (int cnt = 0; cnt < 20; cnt++)
                {
                	calc_fee_elec = (last_fee_elec + fixed_kwh * curr_elec_fee + 5000) / 10000;
                	calc_fee_service = (last_fee_service + fixed_kwh * curr_service_fee + 5000) / 10000;
                	if ((calc_fee_elec + calc_fee_service) == p_account->value)
                		break;
                	else if ((calc_fee_elec + calc_fee_service) > p_account->value)
                	{
                		fixed_kwh -= 10;
                	}
                	else
                	{
                		fixed_kwh += 10;
                	}
                }

                p_fee->total_fee_elec = last_fee_elec + fixed_kwh * curr_elec_fee;
                p_fee->total_fee_service = last_fee_service + fixed_kwh * curr_service_fee;
                p_fee->total_kwh = last_total_kwh + fixed_kwh;
                p_fee->period_power[period_idx] = last_period_kwh + fixed_kwh;

                p_fee->stop_code = CHARGE_STOP_MONEY;
                log_debug("money[%d] exceed [%d], stop charging\r\n", (p_fee->total_fee_elec + p_fee->total_fee_service) / 10000, p_account->value);
                is_stop = 1;
            }
            break;
        case TYPE_TIME:
            if (p_fee->total_time >= p_account->value)
            {
                p_fee->stop_code = CHARGE_STOP_TIME;
                log_debug("time [%d] exceed [%d] and stop charging\r\n", p_fee->total_time, p_account->value);
                is_stop = 1;
            }
            break;
        case TYPE_ELECTRICITY:
            if (p_fee->total_kwh >= p_account->value)
            {
            	// 费用校准，去掉超出的电量，模拟刚好充值的金额
            	reduce_kwh = p_fee->total_kwh - p_account->value;
            	if (reduce_kwh > 0)
            	{
            	    p_fee->total_fee_elec -= reduce_kwh * curr_elec_fee;
            	    p_fee->total_fee_service -= reduce_kwh * curr_service_fee;
            	    p_fee->total_kwh -= reduce_kwh;
            	    p_fee->period_power[period_idx] -= reduce_kwh;
            	}

                p_fee->stop_code = CHARGE_STOP_ELEC;
                log_debug("electricity [%d] exceed [%d] and stop charging\r\n", p_fee->total_kwh, p_account->value);
                is_stop = 1;
            }
            break;
        default :
            break;
        }
    } while (0);

    if (is_stop)
    {
        event_add_common(&charger->event, EVENT_TYPE_CHARGE_STOP, gun->gun_id, NULL, 0);
    }
}

int32_t bill_save(int8_t bill_index)
{
    local_config_t *cfg = &_charger.local_cfg;
    int32_t ret = 0;

    if (bill_index < 0 || bill_index >= OFFLINE_BILLS_CNT)
    {
        log_error("save wrong bill index:%d\r\n", bill_index);
        return -1;
    }

    if (cfg->off_bill[bill_index].status == BILL_ROM)
    {
        log_error("bill index:%d has been saved already\r\n", bill_index);
        return -1;
    }
    cfg->off_bill[bill_index].status = BILL_ROM;
    cfg->off_bill[bill_index].bill[48] = 0;//将账单类型由在线改为离线

    log_debug("save bill index:%d\r\n", bill_index);
    ret = os_persist_data_write(cfg, sizeof(local_config_t));

    if (ret < 0)
    {
        log_error("save bill index:%d failed\r\n", bill_index);
    }
    return ret;
}

int32_t bill_delete(int8_t bill_index)
{
    local_config_t *cfg = &_charger.local_cfg;
    if (bill_index < 0 || bill_index >= OFFLINE_BILLS_CNT)
    {
        log_error("del wrong bill index:%d\r\n", bill_index);
        return -1;
    }
    if (BILL_RAM == cfg->off_bill[bill_index].status)
    {
        cfg->off_bill[bill_index].status = BILL_NOT_SET;
        log_debug("del RAM bill index:%d\r\n", bill_index);
        return 0;
    }
    else
    {
        cfg->off_bill[bill_index].status = BILL_NOT_SET;
        log_debug("del ROM bill index:%d\r\n", bill_index);
        return os_persist_data_write(cfg, sizeof(local_config_t));
    }
}

int8_t bill_compare(net_client_t *client, uint8_t *buf, uint32_t len)
{
    uint32_t len_plain = 0, len_crypt = 0;
    uint8_t *p_plain = NULL, *p_crypt = NULL;
    int8_t i = 0;
    local_config_t *cfg = &(_charger.local_cfg);
    if (buf == NULL || len < SIZE_OF_ONE_BILL)
    {
        log_error("wrong bill data:%d\r\n", len);
        return -1;
    }
    log_debug("bill compare data len:%d\r\n", len);
    p_plain = buf;
    len_plain = len;
#if SUPPORT_CRYPT
    memset(client->crypt_buf, 0, sizeof(client->crypt_buf));
    p_crypt = buf;
    len_crypt = len;
    len_plain = sizeof(client->crypt_buf);
    if (client->decrypt(p_crypt, len_crypt, client->crypt_buf, &len_plain, client->crypt_key) < 0)
    {
        log_error("decrypt data err\r\n");
        return -1;
    }
    else
    {
        p_plain = client->crypt_buf;
    }
#endif

    for (i = 0; i < OFFLINE_BILLS_CNT; i++)
    {
        if (0 == memcmp(cfg->off_bill[i].bill, p_plain, len_plain))
        {
            log_debug("compare and got same bill! index:%d\r\n", i);
            return i;
        }
    }

    log_error("no bill in the memory\r\n");
    return -1;
}

int8_t bill_update(uint8_t *buf, uint32_t len)
{
    int8_t gun_id = 0, gun_index = 0, i = 0, bill_index = 0;
    local_config_t *cfg = &_charger.local_cfg;

    if (buf == NULL || len != SIZE_OF_ONE_BILL)
    {
        log_error("wrong bill data:%d\r\n", len);
        return -1;
    }

    //必须保存过离线费率才能保存离线账单
    if (cfg->magic != LOCAL_CONFIG_MAGIC)
    {
        log_warn("offline bill can not be save without price!\r\n");
        return -1;
    }

    gun_id = buf[GUN_ID_INDEX];
    gun_index = gun_id - 1;

    for (i = 0; i < BILLS_OF_A_GUN; i++)
    {
        bill_index = gun_index + i * CHARGE_GUN_CNT;
        if (cfg->off_bill[bill_index].status == BILL_NOT_SET)
        {
            memcpy(cfg->off_bill[bill_index].bill, buf, SIZE_OF_ONE_BILL);
            cfg->off_bill[bill_index].status = BILL_RAM;
            log_debug("RAM bill index:%d update ok,gun index:%d\r\n", bill_index, gun_index);
            return bill_index;
        }
    }

    if (i == OFFLINE_BILLS_CNT)
    {
        //某个枪的账单已达到最大值，本次账单丢弃
        log_warn("bills of gun id[%d] come to max num[%d], have been discard\r\n", buf[GUN_ID_INDEX], OFFLINE_BILLS_CNT);
    }

    return -1;
}

void bill_send(net_client_t *client)
{
    int i = 0, ret = 0;
    local_config_t *cfg = &_charger.local_cfg;

    for (i = 0; i < OFFLINE_BILLS_CNT; i++)
    {
        if (BILL_ROM == cfg->off_bill[i].status)
        {
            log_debug("got ROM bill! index:%d\r\n", i);
            do
            {
                ret = proto_encode_pack(client, cfg->off_bill[i].bill, SIZE_OF_ONE_BILL, CMD_60);
                log_debug("pack bill and send, ret:%d\r\n", ret);
            } while (ret < 0);
        }
    }
}

// 获取当前的费用信息 add by zh
int get_gun_fee(uint8_t gun_idx, fee_t * p_fee)
{
    fee_t *p_gun_fee = &_charger.gun[gun_idx].fee;

    *p_fee = *p_gun_fee;
    p_fee->price_info = _charger.local_cfg.price; // 20210619 add by cz
    return 0;
}

// 获取账号余额信息  add by zh
int get_gun_account(uint8_t gun_idx, account_t * p_account)
{
    account_t *p_gun_account = &_charger.gun[gun_idx].account;
    
    *p_account = *p_gun_account;

    return 0;
}

static int charge_config_load(local_config_t *cfg)
{
    int ret = 0;

    if (cfg->magic == LOCAL_CONFIG_MAGIC)
    {
        return 0;
    }

    memset(cfg, 0, sizeof(local_config_t));

    ret = os_persist_data_read(cfg, sizeof(local_config_t));
    if (0 != ret)
    {
        return -1;
    }

    if (cfg->magic == LOCAL_CONFIG_MAGIC)
    {
        log_debug("load local config ok\r\n");
    }
    else
    {
        log_debug("nothing to load\r\n");
        return -1;
    }

    return 0;
}

void event_printf(charge_event_t *e)
{
    event_item_t *item = NULL;
    int32_t i = 0;

    for (item = e->start; item < e->end; item++)
    {
        log_debug("e[%d], type:%02x ", i++, item->type);
        if ((item->type & EVENT_SET_BIT) == EVENT_SET_BIT)
        {
            if ((item->type & EVENT_CLR_BIT) == EVENT_TYPE_ALARM)
            {
                log_debug("cmd:%2x, time_out:%d, para:%p, time_last:%d\r\n", item->ev.alarm.cmd, item->ev.alarm.time_out, \
                    item->ev.alarm.param, item->ev.alarm.time_last);
            }
            else
            {
                log_debug("gun id:%d, seq:%d\r\n", item->ev.common.gun_id, item->ev.common.seq);
            }
        }
        else
        {
            log_debug("no set\r\n");
        }
    }

    log_debug("event printf end\r\n");
}

static int32_t event_add(charge_event_t *e, event_item_t *i)
{
    event_item_t *item = NULL;
    OS_MUTEX_LOCK(&e->ev_mutex, EVENT_MUTEX_TIME);

    for (item = e->start; item < e->end; item++)
    {
        if ((item->type & EVENT_SET_BIT) == 0)
        {
            memcpy(item, i, sizeof(event_item_t));
            item->type |= EVENT_SET_BIT;
            OS_MUTEX_UNLOCK(&e->ev_mutex);
            return 0;
        }
    }

    //no space for new item
    log_error("event add err\r\n");
    OS_MUTEX_UNLOCK(&e->ev_mutex);
    return -1;
}

int32_t event_add_common(charge_event_t *e, uint8_t type, uint8_t gun_id, void *param, uint16_t seq)
{
    event_item_t ei;

    ei.type = type;
    ei.ev.common.gun_id = gun_id;
    ei.ev.common.param = param;
    ei.ev.common.seq = seq;
    //log_debug("event_add_common, type:%d, id:%d, seq:%d\r\n", type, gun_id, seq);
    return event_add(e, &ei);
}

/**
 * e        : event handle
 * cmd    : cmd that need to be sent cyclically
 * to       : timer interval
 * lt       :  last sent time
**/
static void event_add_alarm(charge_event_t *e, uint8_t cmd, uint16_t to, void *p, uint32_t lt)
{
    event_item_t ei;

    ei.type = EVENT_TYPE_ALARM;
    ei.ev.alarm.cmd = cmd;
    ei.ev.alarm.time_out = to;
    ei.ev.alarm.time_last = lt;
    ei.ev.alarm.param = p;
    event_add(e, &ei);
}

static void event_del_alarm(charge_event_t *e, void *p)
{
    event_item_t *item = NULL;

    OS_MUTEX_LOCK(&e->ev_mutex, EVENT_MUTEX_TIME);

    for (item = e->start; item < e->end; item++)
    {
        if (item->type == (EVENT_SET_BIT | EVENT_TYPE_ALARM))
        {
            if (item->ev.alarm.param == p)
            {
                item->type &= EVENT_CLR_BIT;
            }
        }
    }

    OS_MUTEX_UNLOCK(&e->ev_mutex);
}

//delete all the event except the bill alarm event
void event_del_all_except_bill(charge_event_t *e)
{
    log_debug("event reset!\r\n");
    event_item_t *item = NULL;

    OS_MUTEX_LOCK(&e->ev_mutex, EVENT_MUTEX_TIME);

    for (item = e->start; item < e->end; item++)
    {
        if (item->type == (EVENT_SET_BIT | EVENT_TYPE_ALARM))  //已经SET的定时任务
        {
            if (item->ev.alarm.cmd != CMD_60)
            {
                item->type &= EVENT_CLR_BIT;
            }
        }
    }

    OS_MUTEX_UNLOCK(&e->ev_mutex);
}

static void event_update_alarm_last_time(charge_event_t *e, event_item_t *i, uint32_t lt)
{
    OS_MUTEX_LOCK(&e->ev_mutex, EVENT_MUTEX_TIME);
    i->ev.alarm.time_last = lt;
    OS_MUTEX_UNLOCK(&e->ev_mutex);
}

static void event_update_alarm_time_out(charge_event_t *e, EV_PARAM_TYPE t, void *p, uint16_t to)
{
    event_item_t *item = NULL;

    OS_MUTEX_LOCK(&e->ev_mutex, EVENT_MUTEX_TIME);

    for (item = e->start; item < e->end; item++)
    {
        if (item->type == (EVENT_SET_BIT | EVENT_TYPE_ALARM))
        {
            if (EV_PARAM_TYPE_CMD == t)
            {
                if (item->ev.alarm.cmd == (uint8_t) * (uint8_t *)(p))
                {
                    item->ev.alarm.time_out = to;
                }
            }
            else if (EV_PARAM_TYPE_GUN == t)
            {
                if (item->ev.alarm.param == p)
                {
                    item->ev.alarm.time_out = to;
                }
            }
        }
    }

    OS_MUTEX_UNLOCK(&e->ev_mutex);
}

static event_item_t *event_iterator_get(charge_event_t *e, event_item_t *i)
{
    event_item_t *item = NULL;

    OS_MUTEX_LOCK(&e->ev_mutex, EVENT_MUTEX_TIME);

    while (((e->curr->type & EVENT_SET_BIT) == 0) && e->curr != e->end)
    {
        e->curr++;
    }

    if (e->curr == e->end)
    {
        e->curr = e->start;
        OS_MUTEX_UNLOCK(&e->ev_mutex);
        return NULL;
    }
    else
    {
        if ((e->curr->type & EVENT_CLR_BIT) != EVENT_TYPE_ALARM)
        {
            e->curr->type &= EVENT_CLR_BIT;
        }
        item = e->curr;
        memset(i, 0, sizeof(event_item_t));
        memcpy(i, item, sizeof(event_item_t));
        e->curr++;
        OS_MUTEX_UNLOCK(&e->ev_mutex);
        return item;
    }
}

static void event_handle(charger_t *charger)
{
    event_item_t *p_item, item;
    charge_event_t *ev = &charger->event;
    net_client_t *client = &charger->net_client;
    uint8_t gun_id = 0, gun_index = 0, cmd = 0, i = 0;
    uint32_t curr_time = 0, card_balance = 0;
    gun_t *curr_gun = NULL;
    uint8_t *pile_cmd = NULL, *lock_swtich = NULL;

    while (p_item = event_iterator_get(ev, &item), NULL != p_item)
    {
        gun_id = item.ev.common.gun_id;
        gun_index = gun_id - 1;
        cmd = item.ev.alarm.cmd;
        curr_gun = &charger->gun[gun_index];
        curr_time = os_get_time();

        switch (item.type & EVENT_CLR_BIT)
        {
        case EVENT_TYPE_CONNECT:
            user_cb_connected(os_get_time());
            for (i = 0; i < CHARGE_GUN_CNT; i++)
            {
                event_add_alarm(&charger->event, CMD_21, charger->local_cfg.param.period_await, &charger->gun[i], 0);
                event_add_alarm(&charger->event, CMD_90, charger->local_cfg.param.period_lock, &charger->gun[i], 0);
            }

            event_add_alarm(&charger->event, CMD_B1, charger->local_cfg.param.period_time_sync, NULL, 0);
            event_add_alarm(&charger->event, CMD_02, charger->local_cfg.param.period_environment, charger->pile, 0);
            event_add_alarm(&charger->event, CMD_F2, charger->local_cfg.param.period_heartbeat, NULL, 0);

            proto_try_enqueue_cmd(client, CMD_F4, NULL);
            proto_try_enqueue_cmd(client, CMD_01, (void *)charger->pile);
            proto_try_enqueue_cmd(client, CMD_71, NULL);
            proto_try_enqueue_cmd(client, CMD_35, (void *)charger);

            bill_send(client);
            break;
        case EVENT_TYPE_REBOOT:
            pile_cmd = (uint8_t *)item.ev.common.param;
            proto_set_ack(item.ev.common.seq);
            proto_try_enqueue_cmd(client, CMD_04, (void *)charger);
            // 先发送应答报文，在执行动作
            user_cb_reboot_pile(pile_cmd[0], &charger->error_code);
            break;
#if SUPPORT_LOCK
        case EVENT_TYPE_LOCK_SWITCH:
            lock_swtich = (uint8_t *)item.ev.common.param;
            user_cb_lock_switch(gun_id, lock_swtich[0], &curr_gun->lock_fault_code);

            //todo 添加地锁错误状态上报
            proto_set_ack(item.ev.common.seq);
            proto_try_enqueue_cmd(client, CMD_92, curr_gun);
            break;
#endif
        case EVENT_TYPE_CHARGE_IDLE:
            event_del_alarm(ev, curr_gun);
            proto_try_enqueue_cmd(client, CMD_21, curr_gun);
            event_add_alarm(ev, CMD_21, charger->local_cfg.param.period_await, curr_gun, curr_time);
            break;
        case EVENT_TYPE_CHARGE_READY:
            proto_try_enqueue_cmd(client, CMD_21, curr_gun);
            proto_try_enqueue_cmd(client, CMD_50, curr_gun);
            break;

        case EVENT_TYPE_CHARGE_BY_CARD:
            if (CARD_AUTH_OK == user_cb_card_auth(curr_gun->card_id, &card_balance))
            {
                curr_gun->start_mode = START_MODE_CARD;
                curr_gun->account.balance = curr_gun->account.value = card_balance;

                if (ERR_PROTOCOL_OFFLINE == proto_try_enqueue_cmd(client, CMD_22, curr_gun))
                {
#if SUPPORT_OFFLINE_CARD
                    //设备离线，进行离线刷卡充电流程
                    charge_start(charger, curr_gun, 0);
#else
                    log_warn("不支持离线刷卡充电\r\n");
#endif
                }
            }
            break;
        case EVENT_TYPE_CHARGE_START://收到0x23或0x26，触发开始充电，此步骤会操作硬件，可能会有一定延时
            charge_start(charger, curr_gun, item.ev.common.seq);
            break;
        case EVENT_TYPE_CHARGE_STOP://APP、刷卡停止，计费余额不足停止，此步骤会操作硬件，可能会有一定延时
            charge_stop(charger, curr_gun, item.ev.common.seq);
            break;
        case EVENT_TYPE_PULL_GUN://拔枪
            if (GUN_STATE_CHARGING == charge_get_gun_state(gun_id))
            {
                curr_gun->fee.stop_code = CHARGE_STOP_PULL_GUN;
                charge_stop(charger, curr_gun, item.ev.common.seq);
            }
            charge_set_gun_state(gun_id, GUN_STATE_IDLE);
            break;
        case EVENT_TYPE_EMERG_BTN://按紧急按键停止
            if (GUN_STATE_CHARGING == charge_get_gun_state(gun_id))
            {
                curr_gun->fee.stop_code = DEV_ERR_3003;
                charge_stop(charger, curr_gun, item.ev.common.seq);
            }
            break;
        case EVENT_TYPE_FAULT://运行时异常(充电/非充电中)
            proto_try_enqueue_cmd(client, CMD_C0, curr_gun);
            if (GUN_STATE_CHARGING == charge_get_gun_state(gun_id))
            {
                curr_gun->fee.stop_code = curr_gun->gun_fault_code;
                if ((curr_gun->gun_fault_code == DEV_ERR_300D) || (curr_gun->gun_fault_code == DEV_ERR_3032))
                {
                	charge_stop(charger, curr_gun, item.ev.common.seq);	// 电表故障需要停止充电，通讯中断也停止充电
                }
                else
                {
                	charge_end(charger, curr_gun, item.ev.common.seq);	// 其他故障装置自行停止充电
                }
            }
            charge_set_gun_state(gun_id, GUN_STATE_FAULT);
            proto_try_enqueue_cmd(client, CMD_21, (void *)curr_gun);
            break;
        case EVENT_TYPE_FAULT_RECOVER:
            proto_try_enqueue_cmd(client, CMD_C1, curr_gun);
            if (GUN_STATE_CHARGING != charge_get_gun_state(gun_id))
            	charge_set_gun_state(gun_id, GUN_STATE_IDLE);//如果枪仍插在车上，则需拔枪再插枪才可充电
            break;
        case EVENT_TYPE_VEHICLE_FAULT:  // 车辆故障，桩本身无故障，可正常继续使用，不设置自身故障码
            proto_try_enqueue_cmd(client, CMD_C0, curr_gun);
            if (GUN_STATE_CHARGING == charge_get_gun_state(gun_id))
            {
                curr_gun->fee.stop_code = curr_gun->gun_fault_code;
                charge_end(charger, curr_gun, item.ev.common.seq);
            }
            charge_set_gun_state(gun_id, GUN_STATE_IDLE);// 如果枪仍插在车上，则需拔枪再插枪才可充电
            proto_try_enqueue_cmd(client, CMD_21, (void *)curr_gun);
            break;
        case EVENT_TYPE_GUN_FAULT_UPLOAD:
        	proto_try_enqueue_cmd(client, CMD_C0, curr_gun);	// just upload the charge err !
        	if (GUN_STATE_CHARGING != charge_get_gun_state(gun_id)) // in no charging status !
        	{
				if ((curr_gun->gun_fault_code >= 0x5000) && (curr_gun->gun_fault_code < 0x6000))
				{
					charge_set_gun_state(gun_id, GUN_STATE_IDLE);// 如果枪仍插在车上，则需拔枪再插枪才可充电
				}
				else
				{
					charge_set_gun_state(gun_id, GUN_STATE_FAULT);
				}
				proto_try_enqueue_cmd(client, CMD_21, (void *)curr_gun);
        	}
        	break;

        case EVENT_TYPE_VEHICLE_AUTO_STOP: // 电充满自动上报 
            charge_end(charger, curr_gun, item.ev.common.seq);
            break;
            
        case EVENT_TYPE_SEND_VERSION:
            proto_try_enqueue_cmd(client, CMD_F4, NULL);
            break;

        case EVENT_TYPE_OTA_RSP:
            proto_try_enqueue_cmd(client, CMD_D2, (void *)&charger->ota_info);
            //如果接收,开始下载
            if (charger->ota_info.rsp_state == OTA_ACCEPT)
            {
                proto_try_enqueue_cmd(client, CMD_D3, (void *)&charger->ota_info);
            }
            break;
        case EVENT_TYPE_OTA_DOWNLOADING:
            proto_try_enqueue_cmd(client, CMD_D3, (void *)&charger->ota_info);
            break;
        case EVENT_TYPE_OTA_DOWNLOAD_DONE:
            proto_try_enqueue_cmd(client, CMD_D5, (void *)&charger->ota_info);
            break;
        case EVENT_TYPE_CHARGE_BY_VIN:
            curr_gun->start_mode = START_MODE_VIN;
            if (ERR_PROTOCOL_OFFLINE == proto_try_enqueue_cmd(client, CMD_2A, curr_gun))
            {
                curr_gun->vin_state = VIN_STATE_UNLOCK;
                log_warn("不支持离线VIN码充电\r\n");
            }
            break;
        case EVENT_TYPE_CHARGE_BY_APP_ACCOUNT:
            curr_gun->start_mode = START_MODE_APP_ACCOUNT;
            if (ERR_PROTOCOL_OFFLINE == proto_try_enqueue_cmd(client, CMD_2C, curr_gun))
            {
                curr_gun->app_state = APP_STATE_UNLOCK;
                log_warn("不支持离线APP账号充电\r\n");
            }
            break;
        case EVENT_TYPE_CHARGE_STOP_BY_APP_ACCOUNT:
            if (ERR_PROTOCOL_OFFLINE == proto_try_enqueue_cmd(client, CMD_2E, curr_gun))
            {
                //to do如何去做
                log_warn("不支持离线APP账号停止充电\r\n");
            }
            break;
        case EVENT_TYPE_ALARM:
            if ((curr_time - item.ev.alarm.time_last) >= item.ev.alarm.time_out)
            {
                //log_debug("get timeout event cmd:%02x, time_out:%d, para:%p, time_last:%d, curr_time:%d\r\n", item.ev.alarm.cmd, item.ev.alarm.time_out,
                //   item.ev.alarm.param, item.ev.alarm.time_last, curr_time);
                event_update_alarm_last_time(ev, p_item, curr_time);
                if (cmd == CMD_60) //此cmd仅作为识别event用
                {
                    bill_calculate(charger, (gun_t *)item.ev.alarm.param);
                }
                else
                {
                    proto_try_enqueue_cmd(client, cmd, item.ev.alarm.param);
                }
            }
            break;
        case EVENT_TYPE_GET_INFO:
            proto_set_ack(item.ev.common.seq);
            proto_try_enqueue_cmd(client, CMD_01, (void *)charger->pile);
            break;
        case EVENT_TYPE_POWER_ALLOC:
            user_cb_power_alloc(gun_id, curr_gun->max_out_power, &curr_gun->gun_fault_code);
            proto_set_ack(item.ev.common.seq);
            proto_try_enqueue_cmd(client, CMD_06, (void *)curr_gun);
            break;
        case EVENT_TYPE_QR_CODE:
            user_cb_qr_code();
            break;
        case EVENT_TYPE_SET_PARAMS:
            cmd = CMD_F2;
            event_update_alarm_time_out(&charger->event, EV_PARAM_TYPE_CMD, &cmd, charger->local_cfg.param.period_heartbeat);
            for (i = 1; i <= CHARGE_GUN_CNT; i++)
            {
                if (GUN_STATE_CHARGING == charge_get_gun_state(i))
                {
                    curr_gun = &charger->gun[i - 1];
                    event_update_alarm_time_out(&charger->event, EV_PARAM_TYPE_GUN, curr_gun, charger->local_cfg.param.period_chargeing);
                }
            }

            user_cb_set_params(&charger->error_code);
            if (param_save(&charger->local_cfg) != 0)
            {
                log_debug("param_save error\n");
            }
            proto_try_enqueue_cmd(client, CMD_08, (void *)charger);
            break;
        case EVENT_TYPE_GUN_RELEASE:
            user_cb_gun_release(gun_id, &charger->gun[gun_id - 1].cmd_response_status);
            proto_set_ack(item.ev.common.seq);
            proto_try_enqueue_cmd(client, CMD_37, (void *)curr_gun);
            break;
        default :
            break;
        }
    }
}

static void event_init(charger_t *charger)
{
    memset(charger->ev_buf, 0, sizeof(charger->ev_buf));
    charger->event.start = charger->event.curr = (event_item_t *)charger->ev_buf;
    charger->event.end = (event_item_t *)(charger->ev_buf + sizeof(charger->ev_buf));
    OS_MUTEX_INIT(&charger->event.ev_mutex);
}

void update_charge_fee_to_main(charger_t *charger, gun_t *curr_gun)
{
    uint8_t gun_id = curr_gun->gun_id;
    MeterData_ql  ql_fee_data;
    fee_t *fee = &charger->gun[gun_id - 1].fee;
    uint8_t which_price;
    cmdlist  cmd;

    ql_fee_data.rateid = time_get_period();
    ql_fee_data.gun_id = gun_id - 1;
    ql_fee_data.cost_elec = fee->total_fee_elec;
    ql_fee_data.cost_server = fee->total_fee_service;
    ql_fee_data.charge_duration = (fee->end_time - fee->start_time);  //单位：s//20210619  fee.total_time;
    ql_fee_data.curkWh[0] = fee->total_kwh;
    //2021-6-18 add by cz
    which_price = fee->price_info.period[ql_fee_data.rateid] & 0x03;//当前时段采用尖、高、平、谷哪种费率
    ql_fee_data.cur_elec = fee->price_info.price_elec[which_price];//当前电费费率是多少

    which_price = (fee->price_info.period[ql_fee_data.rateid] & 0x0C) >> 2;
    ql_fee_data.cur_servicer = fee->price_info.price_service[which_price];

    cmd.cmdtype = SET_BILL;
    cmd.body    = (void*)&ql_fee_data;

    gt_para_from_main.cmd_fun(&cmd);
}

void charge_start(charger_t *charger, gun_t *curr_gun, uint16_t seq)
{
    charge_event_t *ev = &charger->event;
    net_client_t *client = &charger->net_client;
    uint32_t curr_time = 0;
    uint8_t gun_id = curr_gun->gun_id;

    if (GUN_STATE_READY == charge_get_gun_state(gun_id))
    {
    	proto_try_enqueue_cmd(client, CMD_F2, (void*)curr_gun);
        user_cb_start_charging(gun_id, &curr_gun->gun_fault_code);
    }
    else
    {
        curr_gun->gun_fault_code = CHARGE_STATE_ERR;
    }

    if (curr_gun->start_mode == START_MODE_APP)
    {
        proto_set_ack(seq);
        proto_try_enqueue_cmd(client, CMD_27, (void *)curr_gun);
    }

    if (curr_gun->start_mode == START_MODE_PLATFORM)
    {
        proto_set_ack(seq);
        proto_try_enqueue_cmd(client, CMD_31, (void *)curr_gun);
    }

    if (curr_gun->gun_fault_code == CHARGE_STATE_ERR)
    {
        log_warn("the gun is not already\r\n");
        return;
    }

    curr_time = os_get_time();
    event_del_alarm(ev, curr_gun);

    if (curr_gun->gun_fault_code == 0)
    {
        charge_set_gun_state(gun_id, GUN_STATE_CHARGING);

        memset(&curr_gun->fee, 0, sizeof(fee_t));
        memcpy(&curr_gun->fee.price_info, &charger->local_cfg.price, sizeof(curr_gun->fee.price_info));

        curr_gun->fee.start_time = curr_time;
        curr_gun->fee.start_kwh = curr_gun->u_gun->current_charge_kwh;
        proto_try_enqueue_cmd(client, CMD_21, (void *)curr_gun);
        event_add_alarm(ev, CMD_60, charger->local_cfg.param.period_calc_bill, curr_gun, curr_time);//借用 cmd 0x60作定时计算账单用
        event_add_alarm(ev, CMD_51, charger->local_cfg.param.period_chargeing_bms, curr_gun, curr_time);
        event_add_alarm(ev, CMD_21, charger->local_cfg.param.period_chargeing, curr_gun, curr_time);
        log_debug("charge start ok, start mode:%d\r\n", curr_gun->start_mode);
    }
    else
    {
        charge_set_gun_state(gun_id, GUN_STATE_FAULT);

        log_warn("charge start fail, gun_id:%d, fault code:%04x\r\n", gun_id, curr_gun->gun_fault_code);
        curr_gun->fee.stop_code = curr_gun->gun_fault_code;//for cmd 0x24
        proto_try_enqueue_cmd(client, CMD_C0, curr_gun);
        proto_try_enqueue_cmd(client, CMD_24, curr_gun);
        event_add_alarm(ev, CMD_21, charger->local_cfg.param.period_await, curr_gun, curr_time);
    }
}

void charge_stop(charger_t *charger, gun_t *curr_gun, uint16_t seq)
{
    charge_event_t *ev = &charger->event;
    net_client_t *client = &charger->net_client;
    uint32_t curr_time = 0;
    uint8_t gun_id = curr_gun->gun_id;

    set_update_bill_flag(gun_id, 1);

    if (GUN_STATE_CHARGING == charge_get_gun_state(gun_id))
    {
        curr_time = os_get_time();
        curr_gun->fee.end_time = curr_time;
    	// 将最后账单数据刷新到装置
    	update_charge_fee_to_main(charger, curr_gun);
    	proto_try_enqueue_cmd(client, CMD_60, curr_gun);

        user_cb_stop_charging(gun_id, &curr_gun->gun_fault_code);
    }
    else
    {
        curr_gun->gun_fault_code = CHARGE_STATE_ERR;
    }

	log_debug("curr_gun fee stop_code:0x%x gun fault:%d", curr_gun->fee.stop_code, curr_gun->gun_fault_code);

    if (CHARGE_STOP_APP == curr_gun->fee.stop_code)
    {
        proto_set_ack(seq);
        proto_try_enqueue_cmd(client, CMD_29, curr_gun);
    }
    else if (CHARGE_STOP_APP_ACCOUNT == curr_gun->fee.stop_code)
    {
        //do nothing
        
    }
    else if (CHARGE_STOP_PLATFROM == curr_gun->fee.stop_code)
    {
        proto_try_enqueue_cmd(client, CMD_33, curr_gun);
    }
    else
    {
        proto_try_enqueue_cmd(client, CMD_24, curr_gun);
    }

    if (CHARGE_STATE_ERR == curr_gun->gun_fault_code)
    {
    	set_update_bill_flag(gun_id, 0);
        return;
    }

    event_del_alarm(ev, curr_gun);

    if (curr_gun->gun_fault_code == 0)  //正常结束
    {
        charge_set_gun_state(gun_id, GUN_STATE_CHARGE_END);
        curr_gun->card_state = CARD_STATE_UNLOCK;
    }
    else    //异常结束
    {
        charge_set_gun_state(gun_id, GUN_STATE_FAULT);
        curr_gun->fee.stop_code = curr_gun->gun_fault_code;
        proto_try_enqueue_cmd(client, CMD_C0, curr_gun);
    }

    proto_try_enqueue_cmd(client, CMD_21, (void *)curr_gun);
//    proto_try_enqueue_cmd(client, CMD_52, (void *)curr_gun);
//    proto_try_enqueue_cmd(client, CMD_53, (void *)curr_gun);
//    proto_try_enqueue_cmd(client, CMD_54, (void *)curr_gun);

    curr_gun->fee.end_time = curr_time;

    // 将最后账单数据刷新到装置
    update_charge_fee_to_main(charger, curr_gun);

    proto_try_enqueue_cmd(client, CMD_60, curr_gun);
    event_add_alarm(ev, CMD_21, charger->local_cfg.param.period_await, curr_gun, curr_time);
    set_update_bill_flag(gun_id, 0);
}

void charge_end(charger_t *charger, gun_t *curr_gun, uint16_t seq)
{
    charge_event_t *ev = &charger->event;
    net_client_t *client = &charger->net_client;
    uint32_t curr_time = 0;
    uint8_t gun_id = curr_gun->gun_id;

	log_debug("curr_gun fee stop_code:0x%x gun fault:%d", curr_gun->fee.stop_code, curr_gun->gun_fault_code);
	set_update_bill_flag(gun_id, 1);
    if (CHARGE_STOP_APP == curr_gun->fee.stop_code)
    {
        proto_set_ack(seq);
        proto_try_enqueue_cmd(client, CMD_29, curr_gun);
    }
    else if (CHARGE_STOP_APP_ACCOUNT == curr_gun->fee.stop_code)
    {
        //do nothing
        
    }
    else if (CHARGE_STOP_PLATFROM == curr_gun->fee.stop_code)
    {
        proto_try_enqueue_cmd(client, CMD_33, curr_gun);
    }
    else
    {
        proto_try_enqueue_cmd(client, CMD_24, curr_gun);
    }

    if (CHARGE_STATE_ERR == curr_gun->gun_fault_code)
    {
    	set_update_bill_flag(gun_id, 0);
        return;
    }

    curr_time = os_get_time();
    event_del_alarm(ev, curr_gun);

    if (curr_gun->gun_fault_code == 0)  //正常结束
    {
        charge_set_gun_state(gun_id, GUN_STATE_CHARGE_END);
        curr_gun->card_state = CARD_STATE_UNLOCK;
    }
    else    //异常结束
    {
        charge_set_gun_state(gun_id, GUN_STATE_FAULT);
        curr_gun->fee.stop_code = curr_gun->gun_fault_code;
        proto_try_enqueue_cmd(client, CMD_C0, curr_gun);
    }

    proto_try_enqueue_cmd(client, CMD_21, (void *)curr_gun);
//    proto_try_enqueue_cmd(client, CMD_52, (void *)curr_gun);
//    proto_try_enqueue_cmd(client, CMD_53, (void *)curr_gun);
//    proto_try_enqueue_cmd(client, CMD_54, (void *)curr_gun);

    curr_gun->fee.end_time = curr_time;
    proto_try_enqueue_cmd(client, CMD_60, curr_gun);

    // 将最后账单数据刷新到装置
    update_charge_fee_to_main(charger, curr_gun);

    event_add_alarm(ev, CMD_21, charger->local_cfg.param.period_await, curr_gun, curr_time);
    set_update_bill_flag(gun_id, 0);
}

/***********************************************************************************************
                                SDK初始化和任务相关
***********************************************************************************************/
void *charge_task_state(void *param)
{
    charger_t *charger = param;

#if SUPPORT_SYSTEM_OS != NO_OS
    for (;;)
#endif
    {
        event_handle(charger);

#if SUPPORT_SYSTEM_OS != NO_OS
        os_sleep_ms(50U);
#endif
    }
}

void *charge_task_transmit(void *param)
{
    charger_t *charger = param;
    net_client_t *client = &charger->net_client;

#if SUPPORT_SYSTEM_OS != NO_OS
    for (;;)
#endif
    {
        switch (client->net_status)
        {
        case NET_STATUS_TRY_CONNECT:
            net_connect(client);
            break;
        case NET_STATUS_CONNECTED:
            log_debug("try to access server\r\n");
            proto_try_enqueue_cmd(client, CMD_F0, (void *)client);
            client->net_status = NET_STATUS_TRY_ACCESS;
        //注意这里没有break
        case NET_STATUS_TRY_ACCESS:
        case NET_STATUS_ACCESS_ALLOW:
            if (ERR_SUCCESS != net_sync(charger))
            {
                charger->net_client.net_status = NET_STATUS_DISCONNECT;
            }
            break;
        case NET_STATUS_DISCONNECT:
            net_disconnect(client);
            net_diss_handle(charger);
            client->net_status = NET_STATUS_TRY_CONNECT;
            log_debug("tcp disconnect\r\n");
            break;
        default:
            break;
        }
#if SUPPORT_SYSTEM_OS != NO_OS
        os_sleep_ms(10U);
#endif
    }
}

int32_t charge_info_init(charger_t *charger, user_pile_t *pile_info, user_gun_t *u_gun, \
    user_bms_t *bms, user_lock_t *lock)
{
    int32_t gun_index = 0;
    int32_t lock_index = 0;
    gun_t* curr_gun;
    
    charger->pile = pile_info;

    for (gun_index = 0; gun_index < CHARGE_GUN_CNT; gun_index++)
    {
        charger->gun[gun_index].gun_id = gun_index + 1;
        charger->gun[gun_index].u_gun = u_gun + gun_index;
        charger->gun[gun_index].bms = bms + gun_index;
        charger->gun[gun_index].lock = lock + gun_index;
        memset(&charger->gun[gun_index].fee, 0, sizeof(charger->gun[gun_index].fee));
        memset(&charger->gun[gun_index].account, 0, sizeof(charger->gun[gun_index].account));
    }

    for (lock_index = 0; lock_index < CHARGE_GUN_CNT; lock_index++)
    {
        lock[lock_index].id = lock_index + 1;
        lock[lock_index].battery = 0;
        lock[lock_index].state = 0;
    }

    charge_config_load(&charger->local_cfg);
    // 默认配置
    {
        charger->local_cfg.param.period_chargeing     = TIME_15_SEC; //充电枪充电时上报枪状态时间间隔，默认15秒
        charger->local_cfg.param.period_heartbeat     = TIME_20_SEC; //心跳上报周期，默认60秒
        charger->local_cfg.param.heartbeat_timeout_cnt = 3;          //心跳包检测超时次数，默认3次
        charger->local_cfg.param.period_environment   = TIME_5_MIN;  //上报环境数据时间间隔
        charger->local_cfg.param.period_await         = TIME_20_SEC;  //待机时上报枪状态时间间隔
        charger->local_cfg.param.period_chargeing_bms = TIME_15_SEC; //充电时上报BMS状态时间间隔
        charger->local_cfg.param.period_time_sync     = TIME_60_SEC; //网络时间同步指令时间间隔
        charger->local_cfg.param.period_lock          = TIME_5_MIN;  //地锁状态同步时间间隔
        charger->local_cfg.param.period_calc_bill     = 1;           //计算账单费用间隔
        charger->local_cfg.param.cmd_response_timeout = 5;           //发送需要平台回复的数据后，几秒内未收到回复则重发，默认5秒
        charger->local_cfg.param.cmd_resend_times     = 3;           //重发次数，默认3次
        charger->local_cfg.param.log_lvl              = 1;           //TCU日志打印level,1消息,2DEBUG,3告警,4错误
        charger->local_cfg.param.log_up_strategy      = 3;           //TCU日志上传策略,1按时间间隔,2按文件轮转,3 Null不主动上传
        charger->local_cfg.param.log_period           = 30;          //TCU日志上传时间间隔,单位分钟
    }

    charger->error_code = 0x00;

    for (gun_index = 0; gun_index < CHARGE_GUN_CNT; gun_index++)
    {
        curr_gun = &charger->gun[gun_index];
        memcpy(&curr_gun->fee.price_info, &charger->local_cfg.price, sizeof(curr_gun->fee.price_info));
    }
    os_srandom(os_get_time());

    return 0;
}

int32_t charge_run(user_pile_t *pile, user_gun_t *u_gun, user_bms_t *bms, user_lock_t *lock)
{
    if ((NULL != pile) && (NULL != u_gun) && (NULL != bms) && (NULL != lock))
    {
        net_client_init(&(_charger.net_client));
        charge_info_init(&(_charger), pile, u_gun, bms, lock);
        event_init(&_charger);

#if SUPPORT_OS == NO_OS
        if (0 == os_thread_create((int8_t *)"send/recv", &_thread_send_recv, \
                charge_task_transmit, &_charger, os_priority_Normal, CHARGE_THREAD_STACK_SIZE))
        {
            if (0 == os_thread_create((int8_t *)"period", &_thread_period, \
                    charge_task_state, &_charger, os_priority_Normal, CHARGE_THREAD_STACK_SIZE))
            {
                return 0;
            }
        }
#else
        if (0 == os_thread_create((int8_t *)"send/recv", &_thread_send_recv, \
                charge_task_transmit, &_charger, os_priority_Low, CHARGE_THREAD_STACK_SIZE))
        {
            if (0 == os_thread_create((int8_t *)"period", &_thread_period, \
                    charge_task_state, &_charger, os_priority_Low, CHARGE_THREAD_STACK_SIZE))
            {
                return 0;
            }
        }
#endif
    }

    return -1;
}
/***********************************************************************************************/

/***********************************************************************************************
                                枪状态等操作相关函数
***********************************************************************************************/
int32_t charge_get_gun_state(uint8_t gun_id)
{
    uint8_t gun_index = gun_id - 1;
    return _charger.gun[gun_index].gun_state;
}

//改变状态
int32_t charge_set_gun_state(uint8_t gun_id, GUN_STATE_t gun_state)
{
    int ret = 0;
    uint8_t gun_index = gun_id - 1;

    if (gun_index >= CHARGE_GUN_CNT)
    {
        return -1;
    }

    log_debug("charge_set_gun_state == %d\r\n", gun_state);

    _charger.gun[gun_index].gun_state = gun_state;

    if (gun_state == GUN_STATE_READY)
    {
        ret = event_add_common(&_charger.event, EVENT_TYPE_CHARGE_READY, gun_id, NULL, 0);
    }
    else if (gun_state == GUN_STATE_IDLE)
    {
        ret = event_add_common(&_charger.event, EVENT_TYPE_CHARGE_IDLE, gun_id, NULL, 0);
    }

    return ret;
}

int32_t charge_start_by_card(uint8_t gun_id, const uint8_t *card_id)
{
    uint8_t gun_index = gun_id - 1;
    
    if (strlen((const char *)card_id) > CARD_ID_LEN)
    {
        return -1;
    }
    if (charge_get_gun_state(gun_id) != GUN_STATE_READY)
    {
        log_warn("该枪未处于准备充电状态 id:%d\r\n", gun_id);
        return -1;
    }
    if (CARD_STATE_LOCK == _charger.gun[gun_index].card_state)
    {
        log_warn("%d号枪正在准备刷卡充电\r\n", gun_id);
        return -1;
    }
    
    _charger.gun[gun_index].card_state   = CARD_STATE_LOCK;
    _charger.gun[gun_index].account.type = TYPE_MONEY;
    memcpy(_charger.gun[gun_index].card_id, card_id, strlen((const char *)card_id));
    
    return event_add_common(&_charger.event, EVENT_TYPE_CHARGE_BY_CARD, gun_id, NULL, 0);
}

int32_t charge_stop_by_card(uint8_t gun_id, const uint8_t *card_id)
{
    uint8_t gun_index = gun_id - 1;
    
    if (strlen((const char *)card_id) > CARD_ID_LEN)
    {
        return -1;
    }
    if (charge_get_gun_state(gun_id) != GUN_STATE_CHARGING)
    {
        log_warn("该枪未在充电中, id:%d\r\n", gun_id);
        return -1;
    }
    if (memcmp(_charger.gun[gun_index].card_id, card_id, CARD_ID_LEN) != 0)
    {
        log_warn("卡号错误，请使用开启充电的卡停止充电\r\n");
        return -1;
    }
    
    _charger.gun[gun_index].fee.stop_code = CHARGE_STOP_CARD;
    
    return event_add_common(&_charger.event, EVENT_TYPE_CHARGE_STOP, gun_id, NULL, 0);
}

int32_t charge_start_by_vin(uint8_t gun_id, const uint8_t *vin)
{
    uint8_t gun_index = gun_id - 1;

    if (charge_get_gun_state(gun_id) != GUN_STATE_READY)
    {
        log_warn("该枪未处于准备充电状态 id:%d\r\n", gun_id);
        return -1;
    }

    if (VIN_STATE_LOCK == _charger.gun[gun_index].vin_state)
    {
        log_warn("%d号枪正在准备VIN码充电\r\n", gun_id);
        return -1;
    }

    _charger.gun[gun_index].vin_state = VIN_STATE_LOCK;
    _charger.gun[gun_index].account.type = TYPE_MONEY;

    return event_add_common(&_charger.event, EVENT_TYPE_CHARGE_BY_VIN, gun_id, NULL, 0);
}

int32_t charge_stop_by_vin(uint8_t gun_id, const uint8_t *vin)
{
    uint8_t gun_index = gun_id - 1;

    if (vin == NULL)
    {
        log_warn("charge_stop_by_vin par error！\r\n");
        return -1;
    }

    if (charge_get_gun_state(gun_id) != GUN_STATE_CHARGING)
    {
        log_warn("该枪未在充电中, id:%d\r\n", gun_id);
        return -1;
    }

    if (memcmp(_charger.gun[gun_index].bms->brm_vin, vin, BRM_VERSION_LEN) != 0)
    {
        log_warn("VIN错误，正确的为%s, 输入的为%s\r\n", _charger.gun[gun_index].bms->brm_vin, vin);
        return -1;
    }

    _charger.gun[gun_index].fee.stop_code = CHARGE_STOP_PULL_GUN;
    return event_add_common(&_charger.event, EVENT_TYPE_CHARGE_STOP, gun_id, NULL, 0);
}

int32_t charge_start_by_app_account(uint8_t gun_id, const uint8_t *app_account, const uint8_t *app_password)
{
    uint8_t gun_index = gun_id - 1;

    if (app_account == NULL || app_password == NULL)
    {
        log_warn("charge_start_by_app_account par error！\r\n");
        return -1;
    }

    if (charge_get_gun_state(gun_id) != GUN_STATE_READY)
    {
        log_warn("该枪未处于准备充电状态 id:%d\r\n", gun_id);
        return -1;
    }

    if (APP_STATE_LOCK == _charger.gun[gun_index].app_state)
    {
        log_warn("%d号枪正在准备APP账号充电\r\n", gun_id);
        return -1;
    }

    if (strlen(app_account) != APP_ACCOUNT_LEN)
    {
        log_warn("%d号枪APP账号长度不对，需要%d, 实际%d\r\n", APP_ACCOUNT_LEN, strlen(app_account));
        return -1;
    }

    if (strlen(app_password) > APP_PASSWORD_LEN)
    {
        log_warn("%d号枪APP账号面长度不对，需要小于等于%d, 实际%d\r\n", APP_PASSWORD_LEN, strlen(app_password));
        return -1;
    }

    _charger.gun[gun_index].app_state = APP_STATE_LOCK;
    _charger.gun[gun_index].account.type = TYPE_MONEY;

    memcpy(_charger.gun[gun_index].app_account, app_account, strlen(app_account));
    memcpy(_charger.gun[gun_index].app_password, app_password, strlen(app_password));

    return event_add_common(&_charger.event, EVENT_TYPE_CHARGE_BY_APP_ACCOUNT, gun_id, NULL, 0);
}

int32_t charge_stop_by_app_account(uint8_t gun_id, const uint8_t *app_account, const uint8_t *app_password)
{
    uint8_t gun_index = gun_id - 1;

    if (app_account == NULL || app_password == NULL)
    {
        log_warn("charge_stop_by_app_account par error！\r\n");
        return -1;
    }

    if (charge_get_gun_state(gun_id) != GUN_STATE_CHARGING)
    {
        log_warn("该枪未在充电中, id:%d\r\n", gun_id);
        return -1;
    }

    if (strlen(app_account) != APP_ACCOUNT_LEN)
    {
        log_warn("%d号枪APP账号长度不对，需要%d, 实际%d\r\n", APP_ACCOUNT_LEN, strlen(app_account));
        return -1;
    }

    if (strlen(app_password) > APP_PASSWORD_LEN)
    {
        log_warn("%d号枪APP账号面长度不对，需要小于等于%d, 实际%d\r\n", APP_PASSWORD_LEN, strlen(app_password));
        return -1;
    }

    memcpy(_charger.gun[gun_index].app_account, app_account, strlen(app_account));
    memcpy(_charger.gun[gun_index].app_password, app_password, strlen(app_password));

    _charger.gun[gun_index].fee.stop_code = CHARGE_STOP_APP_ACCOUNT;

    return event_add_common(&_charger.event, EVENT_TYPE_CHARGE_STOP_BY_APP_ACCOUNT, gun_id, NULL, 0);
}

int32_t charge_press_emerg_btn(uint8_t gun_id)
{
    return event_add_common(&_charger.event, EVENT_TYPE_EMERG_BTN, gun_id, NULL, 0);
}

int32_t charge_pull_gun(uint8_t gun_id)
{
    return event_add_common(&_charger.event, EVENT_TYPE_PULL_GUN, gun_id, NULL, 0);
}

int32_t charge_fault_occur(uint8_t gun_id, uint16_t fault_code)
{
    uint8_t gun_index = gun_id - 1;

    _charger.gun[gun_index].gun_fault_code = fault_code;
    return event_add_common(&_charger.event, EVENT_TYPE_FAULT, gun_id, NULL, 0);
}

int32_t charge_fault_upload(uint8_t gun_id, uint16_t fault_code)
{
    uint8_t gun_index = gun_id - 1;
    _charger.gun[gun_index].gun_fault_code = fault_code;
    return event_add_common(&_charger.event, EVENT_TYPE_GUN_FAULT_UPLOAD, gun_id, NULL, 0);
}

int32_t charge_vehicle_fault_occur(uint8_t gun_id, uint16_t fault_code)
{
    uint8_t gun_index = gun_id - 1;
    
    _charger.gun[gun_index].gun_fault_code = fault_code;
    return event_add_common(&_charger.event, EVENT_TYPE_VEHICLE_FAULT, gun_id, NULL, 0);
}

int32_t charge_vehicle_auto_stop_occur(uint8_t gun_id, uint16_t stop_code)
{
    uint8_t gun_index = gun_id - 1;
    
     _charger.gun[gun_index].fee.stop_code = stop_code;
     _charger.gun[gun_index].gun_fault_code = 0;
     
    return event_add_common(&_charger.event, EVENT_TYPE_VEHICLE_AUTO_STOP, gun_id, NULL, 0);
}


int32_t charge_fault_recover(uint8_t gun_id)
{
    return event_add_common(&_charger.event, EVENT_TYPE_FAULT_RECOVER, gun_id, NULL, 0);
}
/***********************************************************************************************/


/***********************************************************************************************
                                参数类型转换
***********************************************************************************************/
//转换温度
uint8_t convert_temperature(int8_t temperature)
{
    return temperature + 50;
}

//转换电气参数
uint16_t convert_electric(float elec_param)
{
    return (uint16_t)((elec_param + 0.05f) * 10);
}

//转换BMS电流
uint16_t convert_bms_current(float current)
{
    return (uint16_t)((current + 0.05f + 400) * 10);
}

//转换BMS电压
uint16_t convert_bms_voltage(float voltage)
{
    uint16_t v = (uint16_t)((voltage + 0.05f) * 10);
    return (v * 10);
}
